import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
import { MmLocalStorage } from '@/utils/storage'
import store from '@/store'
import router from '@/router'

class Request<R = any> {
  private instance: AxiosInstance

  constructor(config: AxiosRequestConfig) {
    // 创建 axios 实例
    this.instance = axios.create(config)
    // 全局拦截器
    this.initGlobalInterceptors()
  }

  private initGlobalInterceptors() {
    this.instance.interceptors.request.use(
      (config) => {
        const token: string = MmLocalStorage.get('token') ?? ''

        // 携带 token
        if (token) {
          config.headers.Authorization = `Bearer ${token}`
        }

        return config
      },
      (error) => {
        return error
      }
    )

    // 全局 response 拦截器
    this.instance.interceptors.response.use(
      (response) => {
        if (response.data.code !== 0) {
          return Promise.reject(response.data.message)
        }

        return response.data
      },
      (error) => {
        // 网络错误或跨域错误
        if (!error.response) {
          return Promise.reject(error.message)
        }

        // statusCode: 500, 服务器内部错误
        if (error.response.status === 500) {
          return Promise.reject('服务器错误，请联系管理员')
        }

        // statusCode: 401, 需要登录
        if (error.response.status === 401) {
          store.dispatch('login/logout').then((res) => {
            router.replace(res)
          })
        }

        // 其他异常，返回后端的提示信息
        return Promise.reject(error.response.data.message)
      }
    )
  }

  /**
   * 发送请求
   * @param config 请求配置
   */
  public request<T = R>(config: AxiosRequestConfig): Promise<T> {
    return this.instance.request<any, T>(config)
  }

  /**
   * GET 请求快捷方法
   * @param config 请求配置
   */
  public get<T = R>(config: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: 'GET' })
  }

  /**
   * POST 请求快捷方法
   * @param config 请求配置
   */
  public post<T = R>(config: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: 'POST' })
  }

  /**
   * PUT 请求快捷方法
   * @param config 请求配置
   */
  public put<T = R>(config: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: 'PUT' })
  }

  /**
   * PATCH 请求快捷方法
   * @param config 请求配置
   */
  public patch<T = R>(config: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: 'PATCH' })
  }

  /**
   * DELETE 请求快捷方法
   * @param config 请求配置
   */
  public delete<T = R>(config: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: 'DELETE' })
  }
}

export default Request
